package cn.craccd.mongoHelper.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import cn.craccd.mongoHelper.bean.InitValue;
import cn.craccd.mongoHelper.bean.Page;
import cn.craccd.mongoHelper.bean.SortBuilder;
import cn.craccd.mongoHelper.reflection.ReflectionUtil;
import cn.craccd.mongoHelper.reflection.SerializableFunction;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

/**
 * mongodb操作器
 *
 */
@Service("mongoHelper")
public class MongoHelper implements ApplicationContextAware {
	ApplicationContext applicationContext;
	Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired
	MongoConverter mongoConverter;

	QueryMapper queryMapper;
	UpdateMapper updateMapper;

	@Value("${spring.data.mongodb.print:false}")
	Boolean print;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		if (this.applicationContext == null) {
			this.applicationContext = applicationContext;
		}
	}

	@Value("${spring.data.mongodb.uri}")
	String uri;

	// 主库
	MongoTemplate mongoTemplatePrimary;
	// 从库
	MongoTemplate mongoTemplateSecondary;

	@PostConstruct
	public void init() {
		if (uri.contains("replicaSet")) { // uri配置为集群模式
			// 使用容器中的mongoTemplate,指向主库,支持事务
			this.mongoTemplatePrimary = applicationContext.getBean(MongoTemplate.class);

			// 自己初始化mongoTemplate,指向从库,不支持事务
			if (uri.contains("readPreference")) {
				String[] uriArray = uri.split("&");

				String value = null;
				for (String u : uriArray) {
					if (u.contains("readPreference")) {
						String[] a = u.split("=");
						if (a.length > 1) {
							value = a[1];
						}
						break;
					}
				}
				if (value != null) {
					uri = uri.replace(value, "SecondaryPreferred");
				}
			} else {
				uri = uri + "&readPreference=SecondaryPreferred";
			}
			this.mongoTemplateSecondary = new MongoTemplate(new SimpleMongoClientDatabaseFactory(uri));
		} else { // uri 配置为单机模式

			// 都使用容器中的mongoTemplate
			this.mongoTemplatePrimary = applicationContext.getBean(MongoTemplate.class);
			this.mongoTemplateSecondary = applicationContext.getBean(MongoTemplate.class);
		}

		queryMapper = new QueryMapper(mongoConverter);
		updateMapper = new UpdateMapper(mongoConverter);
	}

	/**
	 * 打印查询语句
	 * 
	 * @param query
	 */
	private void logQuery(Class<?> clazz, Query query) {
		if (print) {
			MongoPersistentEntity<?> entity = mongoConverter.getMappingContext().getPersistentEntity(clazz);
			Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);
			Document mappedField = queryMapper.getMappedObject(query.getFieldsObject(), entity);
			Document mappedSort = queryMapper.getMappedObject(query.getSortObject(), entity);

			String log = "\ndb." + StrUtil.lowerFirst(clazz.getSimpleName()) + ".find(";

			log += FormatUtils.bson(mappedQuery.toJson()) + ")";

			if (!query.getFieldsObject().isEmpty()) {
				log += ".projection(";
				log += FormatUtils.bson(mappedField.toJson()) + ")";
			}

			if (query.isSorted()) {
				log += ".sort(";
				log += FormatUtils.bson(mappedSort.toJson()) + ")";
			}

			if (query.getLimit() != 0l) {
				log += ".limit(" + query.getLimit() + ")";
			}

			if (query.getSkip() != 0l) {
				log += ".skip(" + query.getSkip() + ")";
			}
			log += ";";

			logger.info(log);
		}
	}

	/**
	 * 打印查询语句
	 * 
	 * @param query
	 */
	private void logCount(Class<?> clazz, Query query) {
		if (print) {
			MongoPersistentEntity<?> entity = mongoConverter.getMappingContext().getPersistentEntity(clazz);
			Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);

			String log = "\ndb." + StrUtil.lowerFirst(clazz.getSimpleName()) + ".find(";
			log += FormatUtils.bson(mappedQuery.toJson()) + ")";
			log += ".count();";

			logger.info(log);
		}
	}

	/**
	 * 打印查询语句
	 * 
	 * @param query
	 */
	private void logDelete(Class<?> clazz, Query query) {
		if (print) {
			MongoPersistentEntity<?> entity = mongoConverter.getMappingContext().getPersistentEntity(clazz);
			Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);

			String log = "\ndb." + StrUtil.lowerFirst(clazz.getSimpleName()) + ".remove(";
			log += FormatUtils.bson(mappedQuery.toJson()) + ")";
			log += ";";
			logger.info(log);
		}
	}

	/**
	 * 打印查询语句
	 * 
	 * @param query
	 */
	private void logUpdate(Class<?> clazz, Query query, Update update, boolean multi) {
		if (print) {
			MongoPersistentEntity<?> entity = mongoConverter.getMappingContext().getPersistentEntity(clazz);
			Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity);
			Document mappedUpdate = updateMapper.getMappedObject(update.getUpdateObject(), entity);

			String log = "\ndb." + StrUtil.lowerFirst(clazz.getSimpleName()) + ".update(";
			log += FormatUtils.bson(mappedQuery.toJson()) + ",";
			log += FormatUtils.bson(mappedUpdate.toJson()) + ",";
			log += FormatUtils.bson("{multi:" + multi + "})");
			log += ";";
			logger.info(log);
		}

	}

	/**
	 * 打印查询语句
	 * 
	 * @param object
	 * 
	 * @param query
	 */
	private void logSave(Object object) {
		if (print) {
			String log = "\ndb." + StrUtil.lowerFirst(object.getClass().getSimpleName()) + ".save(";
			log += JSONUtil.toJsonPrettyStr(object);
			log += ");";
			logger.info(log);
		}
	}

	/**
	 * 打印查询语句
	 * 
	 * @param object
	 * 
	 * @param query
	 */
	private void logSave(List<?> list) {
		if (print && list.size() > 0) {
			Object object = list.get(0);

			String log = "\ndb." + StrUtil.lowerFirst(object.getClass().getSimpleName()) + ".save(";
			log += JSONUtil.toJsonPrettyStr(list);
			log += ");";
			logger.info(log);
		}
	}

	/**
	 * 插入或更新
	 * 
	 * @param object 对象
	 */
	public String insertOrUpdate(Object object) {

		Long time = System.currentTimeMillis();
		String id = (String) ReflectUtil.getFieldValue(object, "id");
		Object objectOrg = StrUtil.isNotEmpty(id) ? getMongoTemplate().findById(id, object.getClass()) : null;

		if (objectOrg == null) {
			// 插入
			// 设置插入时间
			if (ReflectUtil.getField(object.getClass(), "createTime") != null) {
				ReflectUtil.setFieldValue(object, "createTime", time);
			}
			if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
				ReflectUtil.setFieldValue(object, "updateTime", time);
			}
			// 设置默认值
			setDefaultVaule(object);
			// 去除id值
			ReflectUtil.setFieldValue(object, "id", null);

			logSave(object);
			mongoTemplatePrimary.save(object);
			id=(String) ReflectUtil.getFieldValue(object, "id");
		} else {
			// 更新
			Field[] fields = ReflectUtil.getFields(object.getClass());
			// 拷贝属性
			for (Field field : fields) {
				if (!field.getName().equals("id") && ReflectUtil.getFieldValue(object, field) != null) {
					ReflectUtil.setFieldValue(objectOrg, field, ReflectUtil.getFieldValue(object, field));
				}
			}

			// 设置更新时间
			if (ReflectUtil.getField(objectOrg.getClass(), "updateTime") != null) {
				ReflectUtil.setFieldValue(objectOrg, "updateTime", time);
			}
			logSave(objectOrg);
			mongoTemplatePrimary.save(objectOrg);
		}
		return  id;
	}

	/**
	 * 插入
	 * 
	 * @param object 对象
	 */
	public String insert(Object object) {
		String id = (String) ReflectUtil.getFieldValue(object, "id");
		Object objectOrg = StrUtil.isNotEmpty(id) ? getMongoTemplate().findById(id, object.getClass()) : null;
		if (objectOrg != null) {
			// 数据库里已有相同id, 去除id以便插入
			ReflectUtil.setFieldValue(object, "id", null);
		}

		insertOrUpdate(object);
		 id = (String) ReflectUtil.getFieldValue(object, "id");
		return id;
	}

	/**
	 * 批量插入
	 * 
	 * @param <T>
	 * 
	 * @param object 对象
	 */
	public <T> void insertAll(List<T> list) {
		Long time = System.currentTimeMillis();

		Map<String, Object> idMap = new HashMap<String, Object>();
		for (Object object : list) {
			if (ReflectUtil.getFieldValue(object, "id") != null) {
				String id = (String) ReflectUtil.getFieldValue(object, "id");
				Object objectOrg = StrUtil.isNotEmpty(id) ? getMongoTemplate().findById(id, object.getClass()) : null;
				idMap.put((String) ReflectUtil.getFieldValue(object, "id"), objectOrg);
			}
		}

		for (Object object : list) {
			if (ReflectUtil.getFieldValue(object, "id") != null && idMap.get((String) ReflectUtil.getFieldValue(object, "id")) != null) {
				// 数据库里已有相同id, 去除id以便插入
				ReflectUtil.setFieldValue(object, "id", null);
			}
			// 设置插入时间
			if (ReflectUtil.getField(object.getClass(), "createTime") != null) {
				ReflectUtil.setFieldValue(object, "createTime", time);
			}
			if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
				ReflectUtil.setFieldValue(object, "updateTime", time);
			}
			// 设置默认值
			setDefaultVaule(object);
		}
		logSave(list);
		mongoTemplatePrimary.insertAll(list);

	}

	/**
	 * 根据id更新
	 * 
	 * @param object 对象
	 */
	public void updateById(Object object) {
		if (StrUtil.isEmpty((String) ReflectUtil.getFieldValue(object, "id"))) {
			return;
		}
		insertOrUpdate(object);
	}

	/**
	 * 根据id更新全部字段
	 * 
	 * @param object 对象
	 */
	public void updateAllColumnById(Object object) {

		if (StrUtil.isEmpty((String) ReflectUtil.getFieldValue(object, "id"))) {
			return;
		}

		Long time = System.currentTimeMillis();
		if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
			ReflectUtil.setFieldValue(object, "updateTime", time);
		}
		logSave(object);
		mongoTemplatePrimary.save(object);

	}

	/**
	 * 根据id删除
	 * 
	 * @param id    对象
	 * @param clazz 类
	 */
	public void deleteById(String id, Class<?> clazz) {

		if (StrUtil.isEmpty(id)) {
			return;
		}
		deleteByQuery(Criteria.where("id").is(id), clazz);
	}

	/**
	 * 根据id删除
	 * 
	 * @param id    对象
	 * @param clazz 类
	 */
	public void deleteByIds(List<String> ids, Class<?> clazz) {

		if (ids == null || ids.size() == 0) {
			return;
		}

		deleteByQuery(Criteria.where("id").in(ids), clazz);
	}

	/**
	 * 根据条件删除
	 * 
	 * @param query 查询
	 * @param clazz 类
	 */
	public void deleteByQuery(Query query, Class<?> clazz) {
		logDelete(clazz, query);
		mongoTemplatePrimary.remove(query, clazz);
	}

	/**
	 * 根据条件删除
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 */
	public void deleteByQuery(Criteria criteria, Class<?> clazz) {
		deleteByQuery(new Query(criteria), clazz);
	}

	/**
	 * 根据条件删除
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 */
	public void deleteByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
		deleteByQuery(new Query(criteriaWrapper.build()), clazz);
	}

	/**
	 * 设置默认值
	 * 
	 * @param object 对象
	 */
	private void setDefaultVaule(Object object) {
		Field[] fields = ReflectUtil.getFields(object.getClass());
		for (Field field : fields) {
			// 获取注解
			if (field.isAnnotationPresent(InitValue.class)) {
				InitValue defaultValue = field.getAnnotation(InitValue.class);

				String value = defaultValue.value();

				if (ReflectUtil.getFieldValue(object, field) == null) {
					// 获取字段类型
					Class<?> type = field.getType();
					if (type.equals(String.class)) {
						ReflectUtil.setFieldValue(object, field, value);
					}
					if (type.equals(Short.class)) {
						ReflectUtil.setFieldValue(object, field, Short.parseShort(value));
					}
					if (type.equals(Integer.class)) {
						ReflectUtil.setFieldValue(object, field, Integer.parseInt(value));
					}
					if (type.equals(Long.class)) {
						ReflectUtil.setFieldValue(object, field, Long.parseLong(value));
					}
					if (type.equals(Float.class)) {
						ReflectUtil.setFieldValue(object, field, Float.parseFloat(value));
					}
					if (type.equals(Double.class)) {
						ReflectUtil.setFieldValue(object, field, Double.parseDouble(value));
					}
					if (type.equals(Boolean.class)) {
						ReflectUtil.setFieldValue(object, field, Boolean.parseBoolean(value));
					}
				}
			}
		}
	}

	/**
	 * 更新查到的第一项
	 * 
	 * @param query  查询
	 * @param update 更新
	 * @param clazz  类
	 */
	public void updateFirst(Query query, Update update, Class<?> clazz) {
		logUpdate(clazz, query, update, false);
		mongoTemplatePrimary.updateFirst(query, update, clazz);
	}

	/**
	 * 更新查到的第一项
	 * 
	 * @param criteria 查询
	 * @param update   更新
	 * @param clazz    类
	 */
	public void updateFirst(Criteria criteria, Update update, Class<?> clazz) {
		updateFirst(new Query(criteria), update, clazz);
	}

	/**
	 * 更新查到的第一项
	 * 
	 * @param criteria 查询
	 * @param update   更新
	 * @param clazz    类
	 */
	public void updateFirst(CriteriaWrapper criteriaWrapper, Update update, Class<?> clazz) {
		updateFirst(new Query(criteriaWrapper.build()), update, clazz);
	}

	/**
	 * 累加某一个字段的数量,原子操作
	 * 
	 * @param object
	 */
	public void addCountById(String id, String property, Number count, Class<?> clazz) {
		Update update = new Update().inc(property, count);
		updateFirst(new Query(Criteria.where("id").is(id)), update, clazz);
	}

	/**
	 * 累加某一个字段的数量,原子操作
	 * 
	 * @param object
	 */
	public <T, R> void addCountById(String id, SerializableFunction<T, R> property, Number count, Class<?> clazz) {
		addCountById(id, ReflectionUtil.getFieldName(property), count, clazz);
	}

	/**
	 * 更新查到的全部项
	 * 
	 * @param query  查询
	 * @param update 更新
	 * @param clazz  类
	 */
	public void updateMulti(Query query, Update update, Class<?> clazz) {
		logUpdate(clazz, query, update, true);
		mongoTemplatePrimary.updateMulti(query, update, clazz);
	}

	/**
	 * 更新查到的全部项
	 * 
	 * @param criteria 查询
	 * @param update   更新
	 * @param clazz    类
	 */
	public void updateMulti(Criteria criteria, Update update, Class<?> clazz) {
		updateMulti(new Query(criteria), update, clazz);
	}

	/**
	 * 更新查到的全部项
	 * 
	 * @param criteria 查询
	 * @param update   更新
	 * @param clazz    类
	 */
	public void updateMulti(CriteriaWrapper criteriaWrapper, Update update, Class<?> clazz) {
		updateMulti(new Query(criteriaWrapper.build()), update, clazz);
	}

	/**
	 * 根据是否在事务中, 选取不同mongoTemplate
	 * 
	 * @return MongoTemplate MongoTemplate
	 */
	private MongoTemplate getMongoTemplate() {
		try {
			if (TransactionAspectSupport.currentTransactionStatus() != null) {
				return mongoTemplatePrimary;
			}
		} catch (NoTransactionException e) {
			return mongoTemplateSecondary;
		}

		return mongoTemplateSecondary;
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param query 查询
	 * @param page  分页
	 * @param clazz 类
	 * @return Page 分页
	 */
	public Page findPage(Query query, Page page, Class<?> clazz) {

		MongoTemplate mongoTemplate = getMongoTemplate();

		// 查询出一共的条数
		Long count = mongoTemplate.count(query, clazz);
		// 查询pagge
		query.skip((page.getCurr() - 1) * page.getLimit());// 从那条记录开始
		query.limit(page.getLimit());// 取多少条记录

		if (!query.isSorted()) {
			query.with(Sort.by(Sort.Direction.DESC, "id"));
		}
		logQuery(clazz, query);
		List<?> list = mongoTemplate.find(query, clazz);

		page.setCount(count);
		page.setRecords(list);

		return page;
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param page     分页
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(Criteria criteria, Page page, Class<?> clazz) {
		return findPage(new Query(criteria), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param page     分页
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(CriteriaWrapper criteriaWrapper, Page page, Class<?> clazz) {
		return findPage(new Query(criteriaWrapper.build()), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(Criteria criteria, Sort sort, Page page, Class<?> clazz) {
		return findPage(new Query(criteria).with(sort), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(Criteria criteria, SortBuilder sortBuilder , Page page, Class<?> clazz) {
		return findPage(new Query(criteria).with(sortBuilder.toSort()), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder , Page page, Class<?> clazz) {
		return findPage(new Query(criteriaWrapper.build()).with(sortBuilder.toSort()), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(CriteriaWrapper criteriaWrapper, Sort sort, Page page, Class<?> clazz) {
		return findPage(new Query(criteriaWrapper.build()).with(sort), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(Sort sort, Page page, Class<?> clazz) {
		return findPage(new Query().with(sort), page, clazz);
	}

	/**
	 * 按查询条件获取Page
	 * 
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return Page 分页
	 */
	public Page findPage(SortBuilder sortBuilder , Page page, Class<?> clazz) {
		return findPage(new Query().with(sortBuilder.toSort()), page, clazz);
	}

	/**
	 * 获取Page
	 * 
	 * @param page  分页
	 * @param clazz 类
	 * @return Page 分页
	 */
	public Page findPage(Page page, Class<?> clazz) {
		return findPage(new Query(), page, clazz);
	}

	/**
	 * 根据id查找
	 * 
	 * @param id    id
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findById(String id, Class<T> clazz) {

		if (StrUtil.isEmpty(id)) {
			return null;
		}

		logQuery(clazz, new Query(Criteria.where("id").is(id)));
		return (T) getMongoTemplate().findById(id, clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(Query query, Class<T> clazz) {
		query.limit(1);
		logQuery(clazz, query);
		return (T) getMongoTemplate().findOne(query, clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param <T>      类型
	 * @param criteria
	 * @param clazz    类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(Criteria criteria, Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteria), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param <T>      类型
	 * @param criteria
	 * @param clazz    类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteriaWrapper.build()), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(Criteria criteria, Sort sort, Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteria).with(sort), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(Criteria criteria, SortBuilder sortBuilder , Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteria).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder , Class<T> clazz) {
		return (T) findOneByQuery(new Query(criteriaWrapper.build()).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(Sort sort, Class<T> clazz) {
		return (T) findOneByQuery(new Query().with(sort), clazz);
	}

	/**
	 * 根据条件查找单个
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return T 对象
	 */
	public <T> T findOneByQuery(SortBuilder sortBuilder , Class<T> clazz) {
		return (T) findOneByQuery(new Query().with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>   类型
	 * @param query 查询
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(Query query, Class<T> clazz) {
		if (!query.isSorted()) {
			query.with(Sort.by(Sort.Direction.DESC, "id"));
		}
		logQuery(clazz, query);
		return (List<T>) getMongoTemplate().find(query, clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(Criteria criteria, Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteria), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteriaWrapper.build()), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(Criteria criteria, SortBuilder sortBuilder , Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteria).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(Criteria criteria, Sort sort, Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteria).with(sort), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder , Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteriaWrapper.build()).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找List
	 * 
	 * @param <T>      类型
	 * @param criteria 查询
	 * @param sort     排序
	 * @param clazz    类
	 * @return List 列表
	 */
	public <T> List<T> findListByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<T> clazz) {
		return (List<T>) findListByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param query         查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T> List<T> findPropertiesByQuery(Query query, Class<?> documentClass, String property, Class<T> propertyClass) {
		query.fields().include(property);
		List<?> list = findListByQuery(query, documentClass);
		List<T> propertyList = extractProperty(list, property, propertyClass);

		return propertyList;
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param query         查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T, R> List<T> findPropertiesByQuery(Query query, Class<?> documentClass, SerializableFunction<T, R> property, Class<T> propertyClass) {
		return findPropertiesByQuery(query, documentClass, ReflectionUtil.getFieldName(property), propertyClass);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public List<String> findPropertiesByQuery(Query query, Class<?> documentClass, String property) {
		return findPropertiesByQuery(query, documentClass, property, String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public <T, R> List<String> findPropertiesByQuery(Query query, Class<?> documentClass, SerializableFunction<T, R> property) {
		return findPropertiesByQuery(query, documentClass, ReflectionUtil.getFieldName(property), String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T> List<T> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property, Class<T> propertyClass) {
		return (List<T>) findPropertiesByQuery(new Query(criteria), documentClass, property, propertyClass);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T, R> List<T> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, SerializableFunction<T, R> property, Class<T> propertyClass) {
		return (List<T>) findPropertiesByQuery(new Query(criteria), documentClass, ReflectionUtil.getFieldName(property), propertyClass);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T, R> List<T> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, SerializableFunction<T, R> property, Class<T> propertyClass) {
		return (List<T>) findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, ReflectionUtil.getFieldName(property), propertyClass);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @param propertyClass 属性类
	 * @return List 列表
	 */
	public <T> List<T> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, String property, Class<T> propertyClass) {
		return (List<T>) findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, property, propertyClass);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public List<String> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property) {
		return findPropertiesByQuery(new Query(criteria), documentClass, property, String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public <T, R> List<String> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, SerializableFunction<T, R> property) {
		return findPropertiesByQuery(new Query(criteria), documentClass, ReflectionUtil.getFieldName(property), String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public List<String> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, String property) {
		return findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, property, String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public <T, R> List<String> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, SerializableFunction<T, R> property) {
		return findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, ReflectionUtil.getFieldName(property), String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public List<String> findPropertiesByIds(Collection<String> ids, Class<?> documentClass, String property) {
		return findPropertiesByQuery(Criteria.where("id").in(ids), documentClass, property, String.class);
	}

	/**
	 * 根据条件查找某个属性
	 * 
	 * @param <T>           类型
	 * @param criteria      查询
	 * @param documentClass 类
	 * @param property      属性
	 * @return List 列表
	 */
	public <T, R> List<String> findPropertiesByIds(Collection<String> ids, Class<?> documentClass, SerializableFunction<T, R> property) {
		return findPropertiesByIds(ids, documentClass, ReflectionUtil.getFieldName(property));
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(Query query, Class<?> clazz) {
		List<String> ids = new ArrayList<String>();
		query.fields().include("id");
		logQuery(clazz, query);
		List<?> list = getMongoTemplate().find(query, clazz);
		for (Object object : list) {
			ids.add((String) ReflectUtil.getFieldValue(object, "id"));
		}

		return ids;
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(Criteria criteria, Class<?> clazz) {
		return findIdsByQuery(new Query(criteria), clazz);
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
		return findIdsByQuery(new Query(criteriaWrapper.build()), clazz);
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(Criteria criteria, Sort sort, Class<?> clazz) {
		return findIdsByQuery(new Query(criteria).with(sort), clazz);
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(Criteria criteria, SortBuilder sortBuilder , Class<?> clazz) {
		return findIdsByQuery(new Query(criteria).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<?> clazz) {
		return findIdsByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
	}

	/**
	 * 根据条件查找id
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return List 列表
	 */
	public List<String> findIdsByQuery(CriteriaWrapper criteriaWrapper, SortBuilder sortBuilder , Class<?> clazz) {
		return findIdsByQuery(new Query(criteriaWrapper.build()).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据id集合查找
	 * 
	 * @param List  ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(Collection<String> ids, Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)), clazz);
	}

	/**
	 * 根据id集合查找
	 * 
	 * @param List  ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(Collection<String> ids, SortBuilder sortBuilder , Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据id集合查找
	 * 
	 * @param List  ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(Collection<String> ids, Sort sort, Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)).with(sort), clazz);
	}

	/**
	 * 根据id集合查找
	 * 
	 * @param Array ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(String[] ids, SortBuilder sortBuilder , Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)).with(sortBuilder.toSort()), clazz);
	}

	/**
	 * 根据id集合查找
	 * 
	 * @param Array ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(String[] ids, Sort sort, Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)).with(sort), clazz);
	}
	
	/**
	 * 根据id集合查找
	 * 
	 * @param Array ids id集合
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findListByIds(String[] ids, Class<T> clazz) {
		return findListByQuery(new Query(Criteria.where("id").in(ids)), clazz);
	}

	/**
	 * 查询全部
	 * 
	 * @param <T>   类型
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findAll(Class<T> clazz) {
		return findListByQuery(new Query(), clazz);
	}

	/**
	 * 查询全部
	 * 
	 * @param <T>   类型
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findAll(Sort sort, Class<T> clazz) {
		return findListByQuery(new Criteria(), sort, clazz);
	}

	/**
	 * 查询全部
	 * 
	 * @param <T>   类型
	 * @param clazz 类
	 * @return List 列表
	 */
	public <T> List<T> findAll(SortBuilder sortBuilder , Class<T> clazz) {
		return findListByQuery(new Criteria(), sortBuilder.toSort(), clazz);
	}

	/**
	 * 查找全部的id
	 * 
	 * @param clazz 类
	 * @return List 列表
	 */
	public List<String> findAllIds(Class<?> clazz) {
		return findIdsByQuery(new Query(), clazz);
	}

	/**
	 * 查找数量
	 * 
	 * @param query 查询
	 * @param clazz 类
	 * @return Long 数量
	 */
	public Long findCountByQuery(Query query, Class<?> clazz) {
		logCount(clazz, query);
		return getMongoTemplate().count(query, clazz);
	}

	/**
	 * 查找数量
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return Long 数量
	 */
	public Long findCountByQuery(Criteria criteria, Class<?> clazz) {
		return findCountByQuery(new Query(criteria), clazz);
	}

	/**
	 * 查找数量
	 * 
	 * @param criteria 查询
	 * @param clazz    类
	 * @return Long 数量
	 */
	public Long findCountByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
		return findCountByQuery(new Query(criteriaWrapper.build()), clazz);
	}

	/**
	 * 查找全部数量
	 * 
	 * @param clazz 类
	 * @return Long 数量
	 */
	public Long findAllCount(Class<?> clazz) {
		return findCountByQuery(new Query(), clazz);
	}

	/**
	 * 获取list中对象某个属性,组成新的list
	 * 
	 * @param list     列表
	 * @param clazz    类
	 * @param property 属性
	 * @return List<T> 列表
	 */
	private <T> List<T> extractProperty(List<?> list, String property, Class<T> clazz) {
		Set<T> rs = new HashSet<T>();
		for (Object object : list) {
			Object value = ReflectUtil.getFieldValue(object, property);
			if (value != null && value.getClass().equals(clazz)) {
				rs.add((T) value);
			}
		}

		return new ArrayList<T>(rs);
	}


}
